Skip to content

Ensure @tailwindcss/cli recovers from a deleted transitive dependency#20137

Merged
RobinMalfait merged 6 commits into
mainfrom
fix/issue-20113
Jun 1, 2026
Merged

Ensure @tailwindcss/cli recovers from a deleted transitive dependency#20137
RobinMalfait merged 6 commits into
mainfrom
fix/issue-20113

Conversation

@RobinMalfait
Copy link
Copy Markdown
Member

This PR fixes an issue where the @tailwindcss/cli can get into a non-recoverable state when any of the transitive dependencies break.

Tailwind CSS has 2 kinds of dependencies:

  1. All your templates
  2. All dependencies that contribute to your configuration such as the input.css, any plugins, any tailwind.config.js files and so on.

When a template changes, we just have to scan for new Tailwind CSS classes and emit a new CSS file. But when the input.css file, or any of its dependencies changes, then we want to perform a full rebuild.

The idea is that your @theme might have changed, or new plugins have been added, or old plugins have been removed.

If you have an input.css file:

@import "tailwindcss";
@config "./tailwind.config.js";

That relies on a custom config: tailwind.config.js:

const theme = require('./my-custom-theme.js');
module.exports = {
  theme
}

If that file relies on yet another file: ./my-custom-theme.js, then changes there should also trigger a full rebuild.

Since we're dealing with JavaScript here, we want to clear the require cache and rebuild the dependency tree such that another change to any of these files triggers a full fresh build.

However, if any of those (transitive) dependencies are deleted, then we will end up in an invalid state. Creating a new compiler will result in a build error. The compiler won't be able to figure out the entire dependency tree, and we're stuck.

Once the user fixes the potentially missing dependency, the watchers will not be watching any of those files because we created a fresh compiler.

With this PR, we fix that by keeping track of old paths and using those while we are still in an invalid state. The moment everything is fixed, a fresh dependency tree is created and everything starts working again without you having to restart the @tailwindcss/cli command.

Fixes: #20113
Closes: #20114
Closes: #20133

Test plan

  • Added an integration test that removes the transitive dependency. Re-adding that file later will recover the CLI state.

@RobinMalfait RobinMalfait requested a review from a team as a code owner June 1, 2026 16:42
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5c744fa1-8fce-4c05-9074-cd7da198858c

📥 Commits

Reviewing files that changed from the base of the PR and between ed0ebb6 and a8c2357.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • packages/@tailwindcss-cli/src/commands/build/index.ts
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/@tailwindcss-cli/src/commands/build/index.ts

Walkthrough

This PR improves CLI watch mode reliability when dependencies change or are deleted. It adds a delete method to the test harness filesystem context, implements structured error formatting with colored output helpers, backs up and restores the compiler's dependency-tracking state (fullRebuildPaths) before and after full rebuilds to recover from errors, treats deleted files as rebuild-triggering events rather than ignoring them, and exercises these changes through test fixture corrections and a new test case for dependency removal scenarios.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: ensuring CLI recovery when a transitive dependency is deleted, which is the core objective of all changes.
Description check ✅ Passed The description comprehensively explains the problem, the solution, and the test plan, directly addressing the issue of CLI entering non-recoverable state when dependencies are deleted.
Linked Issues check ✅ Passed The PR successfully addresses all linked issues: (#20113) rebuilds on deleted dependencies, (#20114) forwards delete events and recovers gracefully, and (#20133) keeps tracking old paths during invalid state.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the deleted dependency recovery issue: test fixtures, test harness utilities, error handling, dependency tracking, and changelog are all aligned with the PR objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/`@tailwindcss-cli/src/commands/build/index.ts:
- Around line 317-323: The current rollback always restores fullRebuildPaths
from backupRebuildPaths on any watch-time exception; change this so the rollback
only occurs if the dependency tree is actually invalid (i.e., if
createCompiler() did not complete successfully). Introduce a local flag (e.g.,
createdCompiler = false) before the try, set createdCompiler = true immediately
after createCompiler() returns, and in the catch or finally restore
fullRebuildPaths from backupRebuildPaths only when createdCompiler is false;
reference the existing variables backupRebuildPaths and fullRebuildPaths and the
createCompiler() call to locate where to add the flag and conditional rollback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 741153da-c4d0-4567-9d8d-4efb417e3d3a

📥 Commits

Reviewing files that changed from the base of the PR and between 6b43b64 and ed0ebb6.

📒 Files selected for processing (3)
  • integrations/cli/index.test.ts
  • integrations/utils.ts
  • packages/@tailwindcss-cli/src/commands/build/index.ts

Comment thread packages/@tailwindcss-cli/src/commands/build/index.ts
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 1, 2026

Confidence Score: 4/5

The recovery logic is correct and the approach is sound, but there is a known runtime issue in the catch block that would prevent the 'Done in' timing line from printing and would cause the new integration test to hang — this was already flagged in a prior review thread and has not yet been addressed.

The backupRebuildPaths snapshot-and-restore mechanism is logically correct across all traced scenarios (first failure, consecutive failures, recovery). The watcher change to enqueue deleted paths is also correct. However, the start timing variable is declared with let inside the try block (line 275), making it out-of-scope in the catch block added by this PR (line 436). Under ES2022 block scoping — which this package targets — evaluating start in the catch body will throw a ReferenceError at runtime, meaning the 'Done in' line never prints and the new test's await process.onStderr((m) => m.includes('Done in')) after the error will hang indefinitely.

packages/@tailwindcss-cli/src/commands/build/index.ts — specifically the catch block around line 436 where start (declared inside the try block) is used.

Reviews (2): Last reviewed commit: "update changelog" | Re-trigger Greptile

Comment thread packages/@tailwindcss-cli/src/commands/build/index.ts
Comment thread packages/@tailwindcss-cli/src/commands/build/index.ts Outdated
@RobinMalfait RobinMalfait merged commit 44818a6 into main Jun 1, 2026
9 checks passed
@RobinMalfait RobinMalfait deleted the fix/issue-20113 branch June 1, 2026 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI watch does not rebuild when a tracked dependency is deleted

1 participant